﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClusteringProblem.Maths.QualityMeasures
{
    public class FMeasure : QualityMeasureStrategy
    {
        public override double Compute(List<Model.IAntType> result, List<Model.Object> source)
        {
            double precision = ComputePrecision(result, source);
            double recall = ComputeRecall(result, source);
            
            return 2 * ((precision * recall) / (precision + recall));            
        }

        private double ComputePrecision(List<Model.IAntType> result, List<Model.Object> source)
        {
            double truePositive = ComputeTruePositive(result, source);
            double falsePositive = ComputeFalsePositive(result, source);

            return truePositive / (truePositive + falsePositive);
        }

        private double ComputeTruePositive(List<Model.IAntType> result, List<Model.Object> source)
        {
            var joinedData = (from c in result
                          join d in source on c.ObjectID equals d.ID.ToString()
                          select new { c.ResultGroupID, d.GroupID }).ToList();

            int size = joinedData.Count;
            int counter = 0;

            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    if (j != i)
                    {
                        if (joinedData[i].GroupID == joinedData[j].GroupID 
                            && joinedData[i].ResultGroupID == joinedData[j].ResultGroupID)
                        {
                            counter++;
                        }
                    }
                }
            }

            return counter;
        }

        private double ComputeFalsePositive(List<Model.IAntType> result, List<Model.Object> source)
        {
            var joinedData = (from c in result
                              join d in source on c.ObjectID equals d.ID.ToString()
                              select new { c.ResultGroupID, d.GroupID }).ToList();

            int size = joinedData.Count;
            int counter = 0;

            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    if (j != i)
                    {
                        if (joinedData[i].GroupID != joinedData[j].GroupID 
                            && joinedData[i].ResultGroupID == joinedData[j].ResultGroupID)
                        {
                            counter++;
                        }
                    }
                }
            }

            return counter;            
        }

        private double ComputeRecall(List<Model.IAntType> result, List<Model.Object> source)
        {
            double truePositive = ComputeTruePositive(result, source);
            double falseNegative = ComputeFalseNegative(result, source);

            return truePositive / (truePositive + falseNegative);            
        }

        private double ComputeFalseNegative(List<Model.IAntType> result, List<Model.Object> source)
        {
            var joinedData = (from c in result
                              join d in source on c.ObjectID equals d.ID.ToString()
                              select new { c.ResultGroupID, d.GroupID }).ToList();

            int size = joinedData.Count;
            int counter = 0;

            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    if (j != i)
                    {
                        if (joinedData[i].GroupID == joinedData[j].GroupID
                            && joinedData[i].ResultGroupID != joinedData[j].ResultGroupID)
                        {
                            counter++;
                        }
                    }
                }
            }

            return counter;            
        }
    }
}
